using System;
using System.Diagnostics.Contracts;
using System.Linq;

namespace ReactiveLang
{
    public class ReactiveProperty<T>: IDisposable
    {
        IDisposable _handle;
        IAssignableExpression<T> _assignable;

        Variable<T> _value;
        public T Value
        {
            get { return _value.Value; }
            set
            {
                Contract.Requires<InvalidOperationException>(!IsReadOnly);
                _assignable.Assign(value);
            }
        }

        public event EventHandler Changed;

        public bool IsReadOnly
        {
            get { return _assignable == null; }
        }

        public ReactiveProperty()
        {
            _value = new Variable<T>();
            _value.Activate(delegate
            {
                var handler = Changed;
                if (handler != null)
                    handler(this, EventArgs.Empty);
            });
            _assignable = _value;
        }
        public ReactiveProperty(T value)
            : this()
        {
            Value = value;
        }
        public ReactiveProperty(IExpression<T> source)
            : this()
        {
            Contract.Requires<ArgumentNullException>(source != null, "source");
            _handle = source.Activate(x => _value.Value = x);
            _assignable = source as IAssignableExpression<T>;
        }
        public void Dispose()
        {
            if (_handle != null)
                _handle.Dispose();
        }

        public IExpression<T> AsExpression()
        {
            IExpression<T> result = _value as IExpression<T>;
            if (_assignable == null)
                return result.HideIdentity();
            else
                return result.MakeWritable(_assignable.Assign);
        }
    }
}